﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Windows.Forms;

namespace FetchAIP
{
    public partial class FetchAIP : Form
    {
        private enum AppStates { Idle, Preparing, Crawling, Downloading, Complete};
        private BackgroundWorker _crawler;
        private BackgroundWorker _downloader;
        
        const string baseURL = "http://www.aip.net.nz/";
        
        public FetchAIP()
        {
            // Build form
            InitializeComponent();

            // Set output path to users desktop + AIP
            this.outputPathTextBox.Text = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\AIP";

            TermsBox terms = new TermsBox();
            terms.ShowDialog();

        }

        // Browse button clicked
        private void OutputPathBrowseButton_Click(object sender, EventArgs e)
        {
            // Create a new instance of FolderBrowserDialog.
            FolderBrowserDialog outputFolderBrowserDialog = new FolderBrowserDialog();

            // A new folder butoon will display in FolderBrowserDialog.
            outputFolderBrowserDialog.ShowNewFolderButton = true;

            // Show FolderBrowserDialog
            DialogResult outputFolderPath = outputFolderBrowserDialog.ShowDialog();

            // Set selected path as Output Folder
            if (outputFolderPath.Equals(DialogResult.OK))
            {
                // Show selected folder path in outputFolderPathTextBox
                outputPathTextBox.Text = outputFolderBrowserDialog.SelectedPath;

                // Start browsing from root folder
                Environment.SpecialFolder rootFolder = outputFolderBrowserDialog.RootFolder;
            }
        }

        // Download button clicked
        private void StartButton_Click(object sender, EventArgs e)
        {
            SetAppState(AppStates.Preparing); // Set form for crawling and download
            bool foldersprepared = Prepare(outputPathTextBox.Text); // Prepare output folder for download
            if (foldersprepared)
            {
                Queue<subPages> subpages = GetOptions(); // Get options
                CrawlPages(subpages); // Crawl pages for links
            }
            else
            {
                SetAppState(AppStates.Idle); // Download complete
            }
        }

        // Prepare output folder for download
        private bool Prepare(string outputFolderPath)
        {
            List<string> folders = new List<string>();

            if (!Directory.Exists(outputFolderPath)) // If output folder path does not exist
            {
                Directory.CreateDirectory(outputFolderPath); // Create output folder
            }

            if (genCheckbox.Checked) // If "General (GEN)" has been selected
            {
                if (Directory.Exists(outputPathTextBox.Text + "\\_AIP_GEN")) // If "_AIP_GEN" folder exists
                {
                    folders.Add("\\_AIP_GEN");
                }
            }

            if (enrCheckbox.Checked) // If "En-route (ENR)" has been selected
            {
                if (Directory.Exists(outputPathTextBox.Text + "\\_AIP_ENR")) // If "_AIP_ENR" folder exists
                {
                    folders.Add("\\_AIP_ENR");
                }
            }

            if (chartsCheckbox.Checked) // If "Aerodrome Charts" has been selected
            {
                if (Directory.Exists(outputPathTextBox.Text + "\\Airport charts")) // If "Airport charts" folder exists
                {
                    folders.Add("\\Airport charts");
                }
            }

            if (ad1Checkbox.Checked) // If "Aerodromes (AD1)" has been selected
            {
                if (Directory.Exists(outputPathTextBox.Text + "\\_AIP_AD1")) // If "_AIP_AD1" folder exists
                {
                    folders.Add("\\_AIP_AD1");
                }
            }

            if (supCheckbox.Checked) // If "Supplements & Circulars" has been selected
            {
                if (Directory.Exists(outputPathTextBox.Text + "\\_AIP_SUP")) // If "_AIP_SUP" folder exists
                {
                    folders.Add("\\_AIP_SUP");
                }
            }
            if (folders.Count != 0)
            {
                var message = new StringBuilder();
                message.Append("The following subfolders exist:\n\n");
                foreach (string folder in folders)
                {
                    message.Append(folder + "\n");
                }
                message.Append("\nAre you sure you want to delete these subfolders?");
                DialogResult DeleteConfirm = MessageBox.Show(message.ToString(), "Confirm Delete!", MessageBoxButtons.YesNo);
                if (DeleteConfirm == DialogResult.Yes)
                {
                    foreach (string folder in folders)
                    {
                        Directory.Delete(outputFolderPath + folder, true);
                    }
                }
                else
                {
                    return false;
                }
            }
            return true;
        }

        // Get options, add URL's to queue
        private Queue<subPages> GetOptions()
        {
            Queue<subPages> subpages = new Queue<subPages>(); // Create a "subpages" queue

            if (genCheckbox.Checked) // If "General (GEN)" has been selected
            {
                subpages.Enqueue(new subPages { PageName = "General", PageURL = "NavWalk.aspx?section=GEN" }); // Add subpage to queue
            }

            if (enrCheckbox.Checked) // If "En-route (ENR)" has been selected
            {
                subpages.Enqueue(new subPages { PageName = "En-route", PageURL = "NavWalk.aspx?section=ENR" }); // Add subpage to queue
            }

            if (chartsCheckbox.Checked) // If "Aerodrome Charts" has been selected
            {
                subpages.Enqueue(new subPages { PageName = "Aerodrome Charts", PageURL = "NavWalk.aspx?section=CHARTS" }); // Add subpage to queue
            }

            if (ad1Checkbox.Checked) // If "Aerodromes (AD1)" has been selected
            {
                subpages.Enqueue(new subPages { PageName = "Aerodromes (AD1)", PageURL = "NavWalk.aspx?section=AD1" }); // Add subpage to queue
            }

            if (supCheckbox.Checked) // If "Supplements & Circulars" has been selected
            {
                subpages.Enqueue(new subPages { PageName = "Home", PageURL = "Home.aspx" }); // Add subpage to queue
            }
            return subpages; // Return a list of pages to be crawled
        }

        private void CrawlPages(Queue<subPages> subpages)
        {
            try
            {
                SetAppState(AppStates.Crawling); // Set application state for crawling

                // Create a background worker
                _crawler = new BackgroundWorker();
                _crawler.WorkerReportsProgress = true;
                _crawler.WorkerSupportsCancellation = true;
                _crawler.DoWork += new DoWorkEventHandler(bgCrawler_DoWork);
                _crawler.ProgressChanged += new ProgressChangedEventHandler(bgCrawler_ProgressChanged);
                _crawler.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgCrawler_RunWorkerCompleted);

                // Declare Tuple object to pass multiple parameters to DoWork method
                Tuple<string, Queue<subPages>> settings = Tuple.Create<string, Queue<subPages>>(baseURL, subpages);

                // Begin download work
                _crawler.RunWorkerAsync(settings);
            }
            catch // if crawling failed for any reason
            {
                SetAppState(AppStates.Idle); // Crawling failed
                throw;
            }
        }

        private void DownloadPDFs(Queue<pdfLinks> pdfs)
        {
            try
            {
                SetAppState(AppStates.Downloading); // Set application state for downloading

                // Create a background worker
                _downloader = new BackgroundWorker();
                _downloader.WorkerReportsProgress = true;
                _downloader.WorkerSupportsCancellation = true;
                _downloader.DoWork += new DoWorkEventHandler(bgDownloader_DoWork);
                _downloader.ProgressChanged += new ProgressChangedEventHandler(bgDownloader_ProgressChanged);
                _downloader.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgDownloader_RunWorkerCompleted);

                // Declare Tuple object to pass multiple parameters to DoWork method
                Tuple<string, Queue<pdfLinks>, string> settings = Tuple.Create<string, Queue<pdfLinks>, string>(baseURL, pdfs, outputPathTextBox.Text);

                // Begin download work
                _downloader.RunWorkerAsync(settings);
            }
            catch // if crawling failed for any reason
            {
                SetAppState(AppStates.Idle); // Downloading failed
                throw;
            }
        }

        // Set new application state, handling button sensitivity, labels, etc.
        private void SetAppState(AppStates newState)
        {
            switch (newState)
            {
                case AppStates.Idle:
                    // Enable form
                    SetFormState(true);
                   
                    cancelButton.Enabled = false;
                    progressBar.Value = 0;
                    
                    genCheckbox.Enabled = true;
                    enrCheckbox.Enabled = true;
                    chartsCheckbox.Enabled = true;
                    ad1Checkbox.Enabled = true;
                    supCheckbox.Enabled = true;
                    statusMessage.Text = "Ready";
                    break;

                case AppStates.Preparing:
                    // Disable form
                    SetFormState(false);
                    
                    cancelButton.Enabled = true;
                    
                    break;

                case AppStates.Crawling:
                    //
                    genCheckbox.Enabled = false;
                    enrCheckbox.Enabled = false;
                    chartsCheckbox.Enabled = false;
                    ad1Checkbox.Enabled = false;
                    supCheckbox.Enabled = false;
                    
                    break;

                case AppStates.Downloading:
                    // 
                    break;
                
                case AppStates.Complete:
                    // 
                    SetFormState(true);
                    
                    cancelButton.Enabled = false;
                    
                    genCheckbox.Checked = false;
                    enrCheckbox.Checked = false;
                    chartsCheckbox.Checked = false;
                    ad1Checkbox.Checked = false;
                    supCheckbox.Checked = false;

                    genCheckbox.Enabled = true;
                    enrCheckbox.Enabled = true;
                    chartsCheckbox.Enabled = true;
                    ad1Checkbox.Enabled = true;
                    supCheckbox.Enabled = true;
                    break;
            }
        }

        // Set Form State
        private void SetFormState(bool status)
        {
            this.outputPathTextBox.Enabled = status; // Set state for output folder path text box
            this.browseButton.Enabled = status; // Set state for browse button
            this.downloadButton.Enabled = status; // Set state for download button
        }

        // Cancel Button
        private void cancelButton_Click(object sender, EventArgs e)
        {
            _crawler.CancelAsync(); // Send cancellation to crawler
            _downloader.CancelAsync(); // Send cancellation to downloader
        }

        private void bgCrawler_DoWork(object sender, DoWorkEventArgs e)
        {
            // Note about exceptions:  If an exception originates anywhere in
            // this method, or methods that it calls, the BackgroundWorker will
            // automatically populate the Error property of the RunWorkerCompletedEventArgs
            // parameter that gets passed into the RunWorkerCompleted event handler.
            // So we can handle the exception in that method.

            Crawl crawler = new Crawl(); // Create crawler
            BackgroundWorker bwCrawler = sender as BackgroundWorker; // Create background worker sender

            // Get Tuple object passed from RunWorkerAsync() method
            Tuple<string, Queue<subPages>> settings = e.Argument as Tuple<string, Queue<subPages>>;

            e.Result = crawler.CrawlAIP(bwCrawler, settings.Item1, settings.Item2); //Start crawler

            // If operation was cancelled (triggered by CancellationPending),
            // we bailed out of ReadTheFile() early.  But still need to set
            // Cancel flag, because RunWorkerCompleted event will still fire.
            if (bwCrawler.CancellationPending)
            {
                e.Cancel = true;
            }
        }

        private void bgCrawler_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // Update status message with number of pages crawled
            this.statusMessage.Text = String.Format("Crawling - {0} pages crawled", e.ProgressPercentage.ToString());
        }

        private void bgCrawler_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if ((e.Cancelled == true))
            {
                this.statusMessage.Text = "Cancelled";
                SetAppState(AppStates.Idle); // Crawling cancelled
            }

            else if (!(e.Error == null))
            {
                this.statusMessage.Text = "Error: Crawl failed";
                SetAppState(AppStates.Idle); // Crawling failed
            }

            else
            {
                try
                {
                    DownloadPDFs((Queue<pdfLinks>)e.Result); // Download PDFs
                }
                catch
                {
                    throw;
                }
            }
            
        }

        private void bgDownloader_DoWork(object sender, DoWorkEventArgs e)
        {
            // Note about exceptions:  If an exception originates anywhere in
            // this method, or methods that it calls, the BackgroundWorker will
            // automatically populate the Error property of the RunWorkerCompletedEventArgs
            // parameter that gets passed into the RunWorkerCompleted event handler.
            // So we can handle the exception in that method.

            Download downloader = new Download();

            // Do the work
            BackgroundWorker bwDownloader = sender as BackgroundWorker;

            // Get Tuple object passed from RunWorkerAsync() method
            Tuple<string, Queue<pdfLinks>, string> settings = e.Argument as Tuple<string, Queue<pdfLinks>, string>;

            e.Result = downloader.DownloadAIP(bwDownloader, settings.Item1, settings.Item2, settings.Item3);

            // If operation was cancelled (triggered by CancellationPending),
            // we bailed out of ReadTheFile() early.  But still need to set
            // Cancel flag, because RunWorkerCompleted event will still fire.
            if (bwDownloader.CancellationPending)
            {
                e.Cancel = true;
            }
        }

        private void bgDownloader_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // Just update progress bar with % complete
            this.progressBar.Value = e.ProgressPercentage;
            this.statusMessage.Text = String.Format("Downloading - {0}%", e.ProgressPercentage.ToString());
        }

        private void bgDownloader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if ((e.Cancelled == true))
            {
                this.statusMessage.Text = "Cancelled";
                SetAppState(AppStates.Idle); // Download cancelled
            }

            else if (!(e.Error == null))
            {
                this.statusMessage.Text = "Error: Dwonload Failed";
                SetAppState(AppStates.Idle); // Download failed
            }

            else
            {
                this.statusMessage.Text = String.Format("Complete - {0} files downloaded", (string)e.Result.ToString());
                SetAppState(AppStates.Complete); // Download finshed
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void helpToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutBox about = new AboutBox();
            about.Show();
        }
    }
}
